import 'package:flutter/material.dart';
import 'dart:math' as math;

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Flutter Lazy 1500 View (Perf Test)')),
        body: const ComposeLazy1500ViewWithString(),
      ),
    );
  }
}

class ComposeLazy1500ViewWithString extends StatefulWidget {
  const ComposeLazy1500ViewWithString({super.key});

  @override
  State<ComposeLazy1500ViewWithString> createState() =>
      _ComposeLazy1500ViewWithStringState();
}

class _ComposeLazy1500ViewWithStringState
    extends State<ComposeLazy1500ViewWithString> {
  static const int totalCount = 1500;
  static const double itemExtent = 100.0; 
  final ScrollController _controller = ScrollController();

  final List<int> allocSizesBytes = const [100, 200, 500, 1000, 2000, 5000];

  int _idx = 0;
  double _lastOffset = 0.0;
  double _accumPx = 0.0;

  @override
  void initState() {
    super.initState();
    _controller.addListener(_onScroll);
  }

  @override
  void dispose() {
    _controller.removeListener(_onScroll);
    _controller.dispose();
    super.dispose();
  }

  void _onScroll() {
    final double offset = math.max(0.0, _controller.offset);
    final double dy = offset - _lastOffset;
    _lastOffset = offset;

    _accumPx += dy.abs();

    if (_accumPx >= 8.0) {
      _accumPx = 0.0;

      final int bytes = allocSizesBytes[_idx % allocSizesBytes.length];
      final String s = createStringOfApproxBytes(bytes);

      final int firstIndex = (offset ~/ itemExtent);
      final int off = (offset % itemExtent).toInt();

      if (_idx % 50 == 0) {
        print(
            'onScroll alloc -> ${bytes}B (len=${s.length})  @index=$firstIndex, off=$off');
      }
      _idx++;
    }
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _controller,
      itemCount: totalCount,
      itemExtent: itemExtent,
      itemBuilder: (context, index) {
        return Padding(
          padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
          child: Container(
            alignment: Alignment.center,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.red, width: 2),
            ),
            child: Text('Item #$index'),
          ),
        );
      },
    );
  }
}


String createStringOfApproxBytes(int bytes) {
  final int chars = (bytes ~/ 2).clamp(1, 1 << 31);
  final sb = StringBuffer();
  for (var i = 0; i < chars; i++) {
    sb.writeCharCode(0x0061); // 'a'
  }
  return sb.toString();
}
